/*-------------------------------------------------------------------------
 *
 * pg_namespace.c
 *	  支持pg_namespace关系操作的常规例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/pg_namespace.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/syscache.h"


/* ----------------
 * NamespaceCreate
 *
 * 使用给定的名称和拥有者 OID 创建一个命名空间（模式）。
 *
 * 如果 isTemp 为真，则该模式是每个后端的模式，用于保存临时表。
 * 目前，它用于防止将其链接为任何活动扩展的成员。
 * （如果有人在扩展脚本中执行 CREATE TEMP TABLE，我们不希望临时模式成为扩展的一部分）。
 * 并且为临时命名空间避免检查默认 ACL（因为没有必要）。
 * ---------------
 */
Oid NamespaceCreate(const char *fc_nspName, Oid fc_ownerId, bool fc_isTemp)
{
	Relation	fc_nspdesc;
	HeapTuple	fc_tup;
	Oid			fc_nspoid;
	bool		fc_nulls[Natts_pg_namespace];
	Datum		fc_values[Natts_pg_namespace];
	NameData	fc_nname;
	TupleDesc	fc_tupDesc;
	ObjectAddress fc_myself;
	int			fc_i;
	Acl		   *fc_nspacl;

	/* 运行检查 */
	if (!fc_nspName)
		elog(ERROR, "no namespace name supplied");

	/* 确保没有同名的现有命名空间 */
	if (SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(fc_nspName)))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_SCHEMA),
				 errmsg("schema \"%s\" already exists", fc_nspName)));

	if (!fc_isTemp)
		fc_nspacl = get_user_default_acl(OBJECT_SCHEMA, fc_ownerId,
									  InvalidOid);
	else
		fc_nspacl = NULL;

	fc_nspdesc = table_open(NamespaceRelationId, RowExclusiveLock);
	fc_tupDesc = fc_nspdesc->rd_att;

	/* 初始化 nulls 和值 */
	for (fc_i = 0; fc_i < Natts_pg_namespace; fc_i++)
	{
		fc_nulls[fc_i] = false;
		fc_values[fc_i] = (Datum) NULL;
	}

	fc_nspoid = GetNewOidWithIndex(fc_nspdesc, NamespaceOidIndexId,
								Anum_pg_namespace_oid);
	fc_values[Anum_pg_namespace_oid - 1] = ObjectIdGetDatum(fc_nspoid);
	namestrcpy(&fc_nname, fc_nspName);
	fc_values[Anum_pg_namespace_nspname - 1] = NameGetDatum(&fc_nname);
	fc_values[Anum_pg_namespace_nspowner - 1] = ObjectIdGetDatum(fc_ownerId);
	if (fc_nspacl != NULL)
		fc_values[Anum_pg_namespace_nspacl - 1] = PointerGetDatum(fc_nspacl);
	else
		fc_nulls[Anum_pg_namespace_nspacl - 1] = true;


	fc_tup = heap_form_tuple(fc_tupDesc, fc_values, fc_nulls);

	CatalogTupleInsert(fc_nspdesc, fc_tup);
	Assert(OidIsValid(fc_nspoid));

	table_close(fc_nspdesc, RowExclusiveLock);

	/* 记录依赖关系 */
	fc_myself.classId = NamespaceRelationId;
	fc_myself.objectId = fc_nspoid;
	fc_myself.objectSubId = 0;

	/* 对所有者的依赖 */
	recordDependencyOnOwner(NamespaceRelationId, fc_nspoid, fc_ownerId);

	/* 对默认 ACL 中提到的角色的依赖关系 */
	recordDependencyOnNewAcl(NamespaceRelationId, fc_nspoid, 0, fc_ownerId, fc_nspacl);

	/* 对扩展的依赖关系 ... 但不适用于神秘的临时模式 */
	if (!fc_isTemp)
		recordDependencyOnCurrentExtension(&fc_myself, false);

	/* 新模式的后创建钩子 */
	InvokeObjectPostCreateHook(NamespaceRelationId, fc_nspoid, 0);

	return fc_nspoid;
}
